﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Diagnostics;
using System.Threading.Tasks;

namespace ALProtocol.Udp
{
    /// <summary>
    /// 网络读取接口。
    /// </summary>
    public interface INetReading
    {
        /// <summary>
        /// 远端地址。
        /// </summary>
        IPEndPoint RemotePoint { get; set; }
        /// <summary>
        /// 接收数据处理。
        /// </summary>
        /// <param name="data">接收到的数据包。</param>
        /// <param name="length">数据包长度。</param>
        void OnHandle(byte[] data, int length);
    }
    /// <summary>
    /// 表示udp通讯通道。
    /// </summary>
    public class UdpChannel : IDisposable
    {
        /// <summary>
        /// socket实例。
        /// </summary>
        Socket m_socket;
        /// <summary>
        /// 处理接口。
        /// </summary>
        INetReading m_handle;
        bool m_isBind = false;
        /// <summary>
        /// 获取本地监听信息。
        /// </summary>
        public IPEndPoint LocalEndPoint
        {
            get { return (IPEndPoint)m_socket.LocalEndPoint; }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsBind
        {
            get
            {
                return m_isBind;
            }
            private set
            {
                m_isBind = value;
            }
        }

        /// <summary>
        /// 构造方法。
        /// </summary>
        /// <param name="recvBuffer">接收数据缓存大小（字节）</param>
        /// <param name="handle">udp处理接口</param>
        public UdpChannel(int recvBuffer, INetReading handle)
        {
            m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            m_socket.ReceiveBufferSize = recvBuffer; // 设置接收UDP数据缓冲区 
            m_handle = handle;
        }

        /// <summary>
        /// 绑定本地端口。
        /// </summary> 
        /// <param name="localPoint">本地终结点信息。</param>
        /// <returns></returns>
        public bool Bind(IPEndPoint localPoint)
        {
            try
            {
                m_socket.Bind(localPoint);
            }
            catch
            {
                return false;
            }
            IsBind = true;
            return true;
        }
        /// <summary>
        /// 发送数据到远端。
        /// </summary>
        /// <param name="data"></param>
        /// <param name="remote"></param>
        /// <returns></returns>
        public bool SendTo(byte[] data, EndPoint remote)
        {
            return m_socket.SendTo(data, remote) > 0;
        }

        /// <summary>
        ///  发送数据列表到远端。
        /// </summary>
        /// <param name="data"></param>
        /// <param name="remote"></param>
        /// <returns></returns>
        public bool SendTo(IList<byte[]> data, EndPoint remote)
        {
            foreach (var item in data)
            {
                if (SendTo(item, remote) == false)
                {
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// 启动数据接收。
        /// </summary>
        public void Start()
        {
            Task task = new Task(Read);
            task.Start();
        }

        /// <summary>
        /// UDP读取线程。
        /// </summary>
        private void Read()
        {
            byte[] buffer = new byte[61440];
            try
            {
                while (true)
                {
                    IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);
                    EndPoint remote = endpoint;
                    int length = m_socket.ReceiveFrom(buffer, ref remote);
                    if (length > 0 && m_handle != null)
                    {
                        m_handle.RemotePoint = (IPEndPoint)remote;
                        m_handle.OnHandle(buffer, length);
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex is SocketException) // 若是socket异常则正常退出
                {
                    return;
                }
                Debug.WriteLine(string.Format("接收UDP数据线程异常退出,{0}", ex.Message));

                Start();
            }
        }
        /// <summary>
        /// 释放。
        /// </summary>
        public void Dispose()
        {
            if (m_socket != null)
            {
                m_socket.Dispose();
            }
        }
    }
}
